package com.github.ecbp.user.service.service.impl;

import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.ecbp.common.constant.CacheKeyConstant;
import com.github.ecbp.common.constant.ConstantCode;
import com.github.ecbp.common.data.entity.BaseEntity;
import com.github.ecbp.common.data.service.impl.BaseServiceImpl;
import com.github.ecbp.common.data.utils.TransformUtils;
import com.github.ecbp.common.redis.RedisUtil;
import com.github.ecbp.common.req.BaseIdListDTO;
import com.github.ecbp.common.req.SimpleIdListStatusParam;
import com.github.ecbp.common.security.utils.JwtTokenUtil;
import com.github.ecbp.common.utils.Asserts;
import com.github.ecbp.common.utils.PlaceHolderUtils;
import com.github.ecbp.common.utils.RandomStringUtils;
import com.github.ecbp.user.service.common.UserCodeAndMessageEnum;
import com.github.ecbp.user.service.dto.AdminRolesDto;
import com.github.ecbp.user.service.entity.TAdmin;
import com.github.ecbp.user.service.entity.TAdminRoleRelation;
import com.github.ecbp.user.service.entity.TRole;
import com.github.ecbp.user.service.mapper.AdminMapper;
import com.github.ecbp.user.service.mapper.RoleMapper;
import com.github.ecbp.user.service.request.AdminReq;
import com.github.ecbp.user.service.request.FindAdminPasswordReq;
import com.github.ecbp.user.service.service.AdminRoleRelationService;
import com.github.ecbp.user.service.service.AdminSaveService;
import com.google.common.collect.ImmutableMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 后台用户表 服务实现类
 * </p>
 *
 * @author zj2626
 * @since 2020-03-18
 */
@Service
public class AdminSaveServiceImpl extends BaseServiceImpl<AdminMapper, TAdmin> implements AdminSaveService {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AdminRoleRelationService adminRoleRelationService;
    @Autowired
    private RoleMapper roleMapper;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public boolean registerUser(AdminReq param) {
        TAdmin admin = TransformUtils.copy(param, TAdmin.class);
        //用户手机不能重复
        checkPhone(admin, -1L, false);

        /**
         * 设置密码
         */
        if (StringUtils.isEmpty(admin.getPassword())) {
            admin.setPassword(RandomStringUtils.getRandomString(8));
        }
        String savingPassword = admin.getPassword();

        //将密码进行加密操作
        String encodePassword = encodePassword(SecureUtil.md5(savingPassword));
        admin.setPassword(encodePassword);
        admin.setStatus(ConstantCode.TRUE);
        int result = baseMapper.insert(admin);
        Asserts.checkHandle(result, UserCodeAndMessageEnum.USER_SAVE_ERROR);

        setDefaultRole(Collections.singletonList(admin.getId()));

        if (logger.isDebugEnabled()) {
            logger.debug("新用户[{}]的密码为[{}]", admin.getLoginPhone(), savingPassword);
        }
        // 发送短信 告知登录密码

        return true;
    }

    /**
     * 修改用户启用状态
     *
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public int updateStatusById(SimpleIdListStatusParam param) {
        LambdaQueryWrapper<TAdmin> wrapper = new LambdaQueryWrapper<>(TAdmin.class);
        wrapper.select(BaseEntity::getId, TAdmin::getLoginPhone);
        wrapper.in(BaseEntity::getId, param.getIds());
        List<TAdmin> admins = baseMapper.selectList(wrapper);
        Asserts.checkListNotNull(admins, UserCodeAndMessageEnum.USER_NOT_EXISTS_ERROR);

        //用户手机不能重复
        if (param.getStatus() == ConstantCode.TRUE) {
            for (TAdmin admin : admins) {
                checkPhone(admin, admin.getId(), false);
            }
        } else {
            for (TAdmin admin : admins) {
                jwtTokenUtil.removeTokenFromRedis(admin.getLoginPhone());
            }
        }

        List<String> updateResult = new ArrayList<>();
        for (TAdmin admin : admins) {
            admin.setStatus(param.getStatus());
            int result = baseMapper.updateById(admin);
            Asserts.checkHandle(result, UserCodeAndMessageEnum.USER_SAVE_ERROR);
            updateResult.add(admin.getLoginPhone());
        }

        logger.info("待启用/禁用的用户:{}", updateResult);
        // 发送短信

        return 1;
    }

    /**
     * 设置默认角色
     *
     * @param adminIds
     */
    private void setDefaultRole(List<Long> adminIds) {
        LambdaQueryWrapper<TRole> wrapper = new LambdaQueryWrapper<>(TRole.class);
        wrapper.select(BaseEntity::getId);
        wrapper.eq(TRole::getIfDefault, true);
        wrapper.eq(TRole::getStatus, ConstantCode.TRUE);
        List<TRole> roleList = roleMapper.selectList(wrapper);
        if (!CollectionUtils.isEmpty(roleList)) {
            List<Long> roleIds = roleList.stream().map(BaseEntity::getId).collect(Collectors.toList());
            for (Long adminId : adminIds) {
                updateRole(new AdminRolesDto(adminId, roleIds));
            }
        }
    }

    /**
     * 修改指定用户的角色信息
     *
     * @param param
     * @return int
     * @author zj2626
     * @date 2020/5/24
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public Set<Long> updateRole(AdminRolesDto param) {
        final Set<Long> oldRoleIds = removeAdminRoleRelation(param.getId());

        //建立新关系
        if (!CollectionUtils.isEmpty(param.getRoleIds())) {
            List<TAdminRoleRelation> adminRoleRelationList = new ArrayList<>();
            for (Long roleId : param.getRoleIds()) {
                TAdminRoleRelation roleRelation = new TAdminRoleRelation();
                roleRelation.setAdminId(param.getId());
                roleRelation.setRoleId(roleId);
                adminRoleRelationList.add(roleRelation);
            }
            boolean result = adminRoleRelationService.saveBatch(adminRoleRelationList, 50);
            Asserts.isTrue(result, UserCodeAndMessageEnum.USER_ROLE_SAVE_ERROR);
        }
        return oldRoleIds;
    }

    private Set<Long> removeAdminRoleRelation(Long adminId) {
        //先删除原有关系
        LambdaQueryWrapper<TAdminRoleRelation> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(TAdminRoleRelation::getId, TAdminRoleRelation::getRoleId);
        wrapper.eq(TAdminRoleRelation::getAdminId, adminId);
        final List<TAdminRoleRelation> tAdminRoleRelations = adminRoleRelationService.list(wrapper);
        if (CollectionUtils.isEmpty(tAdminRoleRelations)) {
            return Collections.emptySet();
        }
        Set<Long> ids = tAdminRoleRelations.stream().map(TAdminRoleRelation::getId).collect(Collectors.toSet());
        adminRoleRelationService.removeByIds(ids);

        return tAdminRoleRelations.stream().map(TAdminRoleRelation::getRoleId).collect(Collectors.toSet());
    }

    /**
     * 检查 用户手机不能重复; 新增: 重复的账号,原账号会状态改为禁用; 修改: 重复的账号,禁止修改
     *
     * @param admin
     * @param id
     */
    private void checkPhone(TAdmin admin, Long id, boolean updateWithoutId) {
        List<TAdmin> adminList = null;
        if (!StringUtils.isEmpty(admin.getLoginPhone())) {
            LambdaQueryWrapper<TAdmin> wrapper = new LambdaQueryWrapper<>(getEntityClass());
            wrapper.select(BaseEntity::getId);
            wrapper.eq(TAdmin::getLoginPhone, admin.getLoginPhone());
            wrapper.eq(TAdmin::getStatus, ConstantCode.TRUE);
            wrapper.last("limit 1");
            adminList = baseMapper.selectList(wrapper);
            if (-1 != id) {
                Asserts.isTrue(CollectionUtils.isEmpty(adminList) || id.equals(adminList.get(0).getId()),
                        UserCodeAndMessageEnum.USER_PHONE_EXISTED_ERROR);
            }
            if (-1 == id && !updateWithoutId) {
                Asserts.isTrue(CollectionUtils.isEmpty(adminList), UserCodeAndMessageEnum.USER_PHONE_EXISTED_ERROR);
            }
        }

        // 需要->禁用旧数据
        if (-1 == id && !CollectionUtils.isEmpty(adminList)) {
            // 用户状态改为禁用
            TAdmin record = new TAdmin();
            record.setId(adminList.get(0).getId());
            record.setStatus(ConstantCode.FALSE);
            baseMapper.updateById(record);
        }
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public int resetPassword(BaseIdListDTO param) {
        LambdaQueryWrapper<TAdmin> wrapper = new LambdaQueryWrapper<>(TAdmin.class);
        wrapper.select(BaseEntity::getId, TAdmin::getLoginPhone);
        wrapper.in(BaseEntity::getId, param.getIds());
        List<TAdmin> admins = baseMapper.selectList(wrapper);
        Asserts.checkListNotNull(admins, UserCodeAndMessageEnum.USER_NOT_EXISTS_ERROR);

        Map<String, String> updateResult = new HashMap<>();
        for (TAdmin admin : admins) {
            String password = RandomStringUtils.getRandomString(8);
            admin.setPassword(encodePassword(SecureUtil.md5(password)));
            updateResult.put(admin.getLoginPhone(), password);
        }
        logger.info("待重置密码的用户:{}", updateResult);
        boolean result = super.updateBatchById(admins, 50);
        Asserts.isTrue(result, UserCodeAndMessageEnum.USER_SAVE_ERROR);

        for (Map.Entry<String, String> entry : updateResult.entrySet()) {
            jwtTokenUtil.removeTokenFromRedis(entry.getKey());

            // 发送短信
        }

        return 1;
    }

    /**
     * 设置密码
     * <p>
     * 1.验证用户存在
     * 2.验证验证码
     * 3.修改密码
     *
     * @param param
     * @return
     */
    @Override
    public Integer findPassword(FindAdminPasswordReq param) {
        // 验证验证码
        ImmutableMap<String, String> immutableMap = ImmutableMap.of(
                CacheKeyConstant.KEY, CacheKeyConstant.CODE_PREFIX,
                CacheKeyConstant.PHONE, param.getLoginPhone());
        String code = redisUtil.get(PlaceHolderUtils.resolverString(CacheKeyConstant.VERIFICATION_IN_REDIS, immutableMap), 0);
        Asserts.checkNotNull(code, UserCodeAndMessageEnum.USER_ICAPTCHA_CODE_ERROR);
        Asserts.isTrue(param.getCode().equalsIgnoreCase(code), UserCodeAndMessageEnum.USER_ICAPTCHA_CODE_ERROR);

        Long id = getIdByLoginPhone(param.getLoginPhone());
        Asserts.checkNotNull(id, UserCodeAndMessageEnum.USER_NOT_EXISTS_ERROR);

        redisUtil.del(PlaceHolderUtils.resolverString(CacheKeyConstant.VERIFICATION_IN_REDIS, immutableMap));
        return findPassword(param, id);

    }

    @Override
    public Integer findPassword(FindAdminPasswordReq param, Long id) {
        // 更新密码
        TAdmin admin = new TAdmin();
        admin.setId(id);
        admin.setPassword(encodePassword(param.getNewPassword()));
        int result = baseMapper.updateById(admin);
        Asserts.checkHandle(result, UserCodeAndMessageEnum.USER_CODE_ERROR);
        return result;
    }

    /**
     * 密码加密
     *
     * @param password
     * @return
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(jwtTokenUtil.getSalt() + password);
    }

    private Long getIdByLoginPhone(String loginPhone) {
        LambdaQueryWrapper<TAdmin> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(BaseEntity::getId);
        wrapper.eq(TAdmin::getLoginPhone, loginPhone);
        wrapper.eq(TAdmin::getStatus, ConstantCode.TRUE);
        wrapper.last("limit 1");
        List<TAdmin> adminList = baseMapper.selectList(wrapper);
        return CollectionUtils.isEmpty(adminList) ? null : adminList.get(0).getId();
    }
}
